pub mod blueprint_error;
pub mod convert_error;
pub mod operation_error;
pub mod request_error;
pub mod not_found_error;

use snafu::prelude::*;
use std::sync::Arc;
use tonic::Status;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum ParrotError {
    #[snafu(display("Invalid request: {source}"))]
    Request { source: request_error::RequestError },

    #[snafu(display("An error occurred when using blueprint: {source}"))]
    Blueprint {
        source: blueprint_error::BlueprintError,
    },

    #[snafu(display("An error occurred when performing operate: {source}"))]
    Operation { source: operation_error::OperationError },

    #[snafu(display("Failed to convert data: {source}"))]
    Convert { source: convert_error::ConvertError },

    #[snafu(display("Can not complete the operation: {message}"))]
    InvalidOperation { message: String },

    #[snafu(whatever, display("{message}"))]
    Whatever {
        message: String,
        #[snafu(source(from(Box<dyn std::error::Error + Send + Sync>, Some)))]
        source: Option<Box<dyn std::error::Error + Send + Sync>>,
    },
}

impl From<ParrotError> for Status {
    fn from(opt_error: ParrotError) -> Self {
        match opt_error {
            ParrotError::Request { source } => Status::invalid_argument(format!("{source}")),
            ParrotError::Blueprint { source } => {
                let mut status = Status::internal(format!("{source}"));
                status.set_source(Arc::new(source));
                status
            }
            ParrotError::Operation { source } => {
                let mut status = Status::internal(format!("{source}"));
                status.set_source(Arc::new(source));
                status
            }
            ParrotError::Convert { source } => {
                let mut status = Status::internal(format!("{source}"));
                status.set_source(Arc::new(source));
                status
            }
            ParrotError::InvalidOperation { message } => Status::failed_precondition(message),
            ParrotError::Whatever { message, source } => {
                let mut status = Status::internal(message);
                if let Some(s) = source {
                    status.set_source(s.into());
                }
                status
            }
        }
    }
}
